﻿/*
	Copyright (C) 2019 Zongming Liu <1849059316@qq.com>

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.
	
	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.
	
	You should have received a copy of the GNU General Public License along
	with this program; if not, write to the Free Software Foundation, Inc.,
	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "InputEventDispatcher.h"
#include "EventMouse.h"
#include "EventListenerMouse.h"
#include "Debuger.h"
#include "../ui/UiCore.h"
#include "CharacterEvent.h"
#include "Eventkey.h"
#include <algorithm>

namespace {
	 int number = 0x10000000;
	 int mouseListenerId = number * 0;
	 int keybordListenerId = number * 1;
}

static const unsigned int removeFlag = 0x01000000;
static const unsigned int mouseListenerRemoveFlag = 0x00000001|removeFlag;
static const unsigned int keybordListenerRemoveFlag = 0x00000010|removeFlag;
static const unsigned int allListenerRemoveFlag = 0x10000000|removeFlag;

namespace SkyEngine2d
{


static int allocateListenerId(InputEventListener::Type type)
{
	switch (type)
	{
	case InputEventListener::Type::EventListenerType_Keybord:
		return keybordListenerId++;
		break;
	case InputEventListener::Type::EventListenerType_Mouse:
		return mouseListenerId++;
		break;
	default:
		return -1;
		break;
	}
}

static InputEventListener::Type getListenerTypeById(int id)
{
	if (id>=number*0&&id<number*1)
	{
		return InputEventListener::Type::EventListenerType_Mouse;
	}
	else if(id >=number*1&& id< number*2)
	{
		return InputEventListener::Type::EventListenerType_Keybord;
	}
	else
	{
		return InputEventListener::Type::EventListenerType_Unknown;
	}
}


	
	InputEventDispatcher * InputEventDispatcher::getInstance()
	{
		static InputEventDispatcher e;
		return &e;
	}
	
	InputEventDispatcher::~InputEventDispatcher()
	{
	
	}
	
	int InputEventDispatcher::addListener(Node* target, const std::shared_ptr<InputEventListener> & listenter)
	{
		int id = -1;
	
		listenter->setAssociatedNode(target);
		listenter->setPriority(0);
	
		switch (listenter->getType())
		{
		case InputEventListener::Type::EventListenerType_Mouse:
		{
			id = allocateListenerId(InputEventListener::Type::EventListenerType_Mouse);
			m_mouseListener.emplace(id, std::make_unique<InputEventDispatcher::Listener>(listenter));
			break;
		}
		case InputEventListener::Type::EventListenerType_Keybord:
		{
			id = allocateListenerId(InputEventListener::Type::EventListenerType_Keybord);
			m_keyboardListener.emplace(id, std::make_unique<InputEventDispatcher::Listener>(listenter));
			break;
		}
		default:
			
			break;
		};
	
		//加入 target -> id 的 映射
		auto & f = m_listenerIds.find(target);
		if (f!=m_listenerIds.end())
		{
			f->second.push_back(id);
		}
		else
		{
			m_listenerIds.emplace(target, std::vector<int>{id});
		}
	
		return id;
	}
	
	void InputEventDispatcher::removeListenerAllByTarget(const Node* tagert)
	{
		auto &f = m_listenerIds.find(tagert);
		if (f != m_listenerIds.end())
		{
			for (int id : f->second)
			{
				this->removeListener(id);
			}
		}
	}
	
	void InputEventDispatcher::removeListener(int id)
	{
		auto type = getListenerTypeById(id);
		switch (type)
		{
		case InputEventListener::Type::EventListenerType_Unknown:
			break;
		case InputEventListener::Type::EventListenerType_Keybord:
		{
			auto &f = m_keyboardListener.find(id);
			if (f != m_keyboardListener.end())
			{
				if (m_isDispatch)  //如果正在分发  这采用标记删除的方式
				{
					f->second->isRemove = true;
					m_removeFlag |= keybordListenerRemoveFlag;
				}
				else
				{
					auto l = f->second->listener;
					auto node = l->getAssociatedNode();
					if (node)
					{
						auto & f = m_listenerIds.find(node);
						if (f != m_listenerIds.end())
						{
							auto iter = std::remove_if(f->second.begin(), f->second.end(), [id](int i) {return i == id; });
							if (iter!=f->second.end())
							{
								f->second.erase(iter);
							}
							if (f->second.size() == 0)
							{
								m_listenerIds.erase(node);
							}
						}
					}
					//直接删除
					m_mouseListener.erase(id);
				}
			}
			break;
		}
		case InputEventListener::Type::EventListenerType_Mouse:
		{
			auto &f = m_mouseListener.find(id);
			if (f != m_mouseListener.end())
			{
				if (m_isDispatch)  //如果正在分发  这采用标记删除的方式
				{
					f->second->isRemove = true;
					m_removeFlag |= mouseListenerRemoveFlag;
				}
				else
				{
					auto l = f->second->listener;
					auto node = l->getAssociatedNode();
					if (node)
					{
						auto & f = m_listenerIds.find(node);
						if (f!=m_listenerIds.end())
						{
							auto iter = std::remove_if(f->second.begin(), f->second.end(), [id](int i) {return i == id; });
							if (iter != f->second.end())
							{
								f->second.erase(iter);
							}
							if (f->second.size()==0)
							{
								m_listenerIds.erase(node);
							}
						}
					}
					//直接删除
					m_mouseListener.erase(id);
				}
			}
			break;
		}
		default:
			break;
		}
	}
	
	void InputEventDispatcher::removeListenerAll()
	{
		if (m_isDispatch)
		{
			m_removeFlag = allListenerRemoveFlag;
		}
		else
		{
			m_mouseListener.clear();
			m_listenerIds.clear();
		}
	}
	
	InputEventDispatcher::InputEventDispatcher()
		:m_mouseEvent(nullptr)
	{
		m_mouseEvent = new EventMouse(EventMouse::MouseEventType::None);
		m_keyEvent = new EventKey(EventKey::Type::None,0);
		m_uiCore = UiCore::getInstance();
	}
	
	void InputEventDispatcher::resetRemoveFlag()
	{
		m_removeFlag = 0x00;
	}
	
	void InputEventDispatcher::dispatchEvent(InputEvent * e)
	{
		m_isDispatch = true;
	
		auto type = e->getType();
		switch (type)
		{
		case IEvent::Type::EventType_Mouse:
		{
			for (auto & l : m_mouseListener)
			{
				if (!l.second->isRemove)
				{
					std::shared_ptr<InputEventListener >&listener = l.second->listener;
					e->setTarget(listener->getAssociatedNode());
					listener->onEvent(e);
				}
			}
			break;
		}
		case IEvent::Type::EventType_Keyboard:
			for (auto & l : m_keyboardListener)
			{
				if (!l.second->isRemove)
				{
					auto & listener = l.second->listener;
					e->setTarget(listener->getAssociatedNode());
					listener->onEvent(e);
				}
			}
			break;
		case IEvent::Type::EventType_User:
			break;
		default:
			break;
		}
	
		m_isDispatch = false;
	
		//处理删除
		if (m_removeFlag&removeFlag)
		{
			if (m_removeFlag&mouseListenerRemoveFlag)
			{
				auto iter = m_mouseListener.begin();
				for (;iter!=m_mouseListener.end();)
				{
					if (iter->second->isRemove)
					{
						iter = m_mouseListener.erase(iter);
					}
					else
					{
						++iter;
					}
				}
			}
			else if(m_removeFlag&keybordListenerRemoveFlag)
			{
	
			}
			else if(m_removeFlag&allListenerRemoveFlag)
			{
				m_mouseListener.clear();
				m_listenerIds.clear();
			}
	
			//重置标记
			this->resetRemoveFlag();
		}
	}
	
	void InputEventDispatcher::dispatchMouseEvent(HWND hwnd, UINT msg, float x, float y)
	{
		switch (msg)
		{
		case WM_RBUTTONDOWN:
		{
			m_mouseEvent->reset(EventMouse::MouseEventType::Down,x,y, EventMouse::ButtonType::Right);
			if (!m_uiCore->onMousePressEvent(m_mouseEvent))
			{
				this->dispatchEvent(m_mouseEvent);
			}
			break;
		}
		case WM_LBUTTONDOWN:
		{
			m_mouseEvent->reset(EventMouse::MouseEventType::Down,x,y, EventMouse::ButtonType::Left);
			if (!m_uiCore->onMousePressEvent(m_mouseEvent))
			{
				this->dispatchEvent(m_mouseEvent);
			}
			break;
		}
		case WM_RBUTTONUP:
		{
			m_mouseEvent->reset(EventMouse::MouseEventType::Up, x,y,EventMouse::ButtonType::Right);
			if (!m_uiCore->onMouseReleaseEvent(m_mouseEvent))
			{
				this->dispatchEvent(m_mouseEvent);
			}
			break;
		}
		case WM_LBUTTONUP:
		{
			m_mouseEvent->reset(EventMouse::MouseEventType::Up,x,y, EventMouse::ButtonType::Left);
			if (!m_uiCore->onMouseReleaseEvent(m_mouseEvent))
			{
				this->dispatchEvent(m_mouseEvent);
			}
			break;
		}
		case WM_MOUSEMOVE:
		{
			m_mouseEvent->reset(EventMouse::MouseEventType::Move, x, y, EventMouse::ButtonType::None);
			if (!m_uiCore->onMouseMoveEvent(m_mouseEvent))
			{
				this->dispatchEvent(m_mouseEvent);
			}
			break;
		}
		case WM_LBUTTONDBLCLK:
		{
			m_mouseEvent->reset(EventMouse::MouseEventType::DoubleDown, x, y, EventMouse::ButtonType::Left);
			if (!m_uiCore->onMouseDoubleClickedEvent(m_mouseEvent))
			{
				this->dispatchEvent(m_mouseEvent);
			}
			break;
		}
		case WM_RBUTTONDBLCLK:
		{
			m_mouseEvent->reset(EventMouse::MouseEventType::DoubleDown, x, y, EventMouse::ButtonType::Right);
			if (!m_uiCore->onMouseDoubleClickedEvent(m_mouseEvent))
			{
				this->dispatchEvent(m_mouseEvent);
			}
			break;
		}
	
		default:
			return;
		}
	}
	
	void InputEventDispatcher::dispatchKeyEvent(HWND hwnd, UINT msg, WPARAM key)
	{
		if (msg==WM_KEYDOWN)
		{
			m_keyEvent->reset(EventKey::Type::KeyPress, key);
			if (!m_uiCore->onKeyPressEvent(m_keyEvent))
			{
				this->dispatchEvent(m_keyEvent);
			}
		}
		else if(msg == WM_KEYUP)
		{
			m_keyEvent->reset(EventKey::Type::KeyRelease, key);
			if (!m_uiCore->onKeyReleaseEvent(m_keyEvent))
			{
				this->dispatchEvent(m_keyEvent);
			}
		}
	}
	
	void InputEventDispatcher::dispatchCharterEvent(wchar_t ch)
	{
		EventCharacter e(ch);
		m_uiCore->onCharacterEvent(&e);
	}
}
